#include "../../juce/juce_amalgamated.h"
#include "MyWaveformComponent.h"

// shouldn't really have this as a define
#define RULERHEIGHT 14
#define RULERWIDTH  24



// MyWaveformHorizontalRuler //

MyWaveformHorizontalRuler::MyWaveformHorizontalRuler(bool isAbove_)
	:	isAbove(isAbove_)
{	
}

void MyWaveformHorizontalRuler::paint(Graphics& g)
{
	g.setColour(Colours::grey);
	g.drawRect(0, 0, getWidth(), getHeight());
	
	g.setColour(Colours::white);
	
	Font font(9);
	g.setFont(font);
	
	double samplesPerPixel = getParentComponent()->getSamplesPerPixel();
	double samplePosition = getParentComponent()->getQuantisedSamplesOffset();
	
	int divisionIncrement = chooseBestDivisionSize(samplesPerPixel);
	int subdivisionIncrement = divisionIncrement/10;
	
	int nextDivision = 0, 
		nextSubdivision = 0;
	
	while(nextDivision < samplePosition)
		nextDivision += divisionIncrement;
	
	while(nextSubdivision < samplePosition)
		nextSubdivision += subdivisionIncrement;
	
	
	//printf("MyWaveformHorizontalRuler::paint(nextDivision=%d nextSubdivision=%d)\n", nextDivision, nextSubdivision);
	
	bool doneFirstMark = false;
	
	Justification just = isAbove ? Justification::topLeft : Justification::bottomLeft;
	
	for(int i = 0; i < getWidth(); i++, samplePosition += samplesPerPixel) {
		
		int iSamplePosition = (int)samplePosition; //roundDoubleToInt(samplePosition);
		
		if((doneFirstMark==false && iSamplePosition==nextDivision)
		    ||
		   (iSamplePosition > nextDivision)) 
		{
			g.drawVerticalLine(i-1,0,getHeight());
						
			g.drawText(String(nextDivision), i+1, 1, 0xffffff, getHeight()-2, just, false);
			
			nextSubdivision = nextDivision;
			nextDivision += divisionIncrement;
			nextSubdivision += subdivisionIncrement;
			
			doneFirstMark == true;
		} 
		else if((doneFirstMark==false && iSamplePosition==nextSubdivision)
				 ||
				(iSamplePosition > nextSubdivision)) 
		{
			if(isAbove)
				g.drawVerticalLine(i-1,3*getHeight()/4, getHeight());
			else
				g.drawVerticalLine(i-1,0,getHeight()/4);
			
			nextSubdivision += subdivisionIncrement;
			
			doneFirstMark == true;
		}
	}
}

int MyWaveformHorizontalRuler::chooseBestDivisionSize(double samplesPerPixel)
{
	//int multiplySamplesPerPixel = (pow(samplesPerPixel, 2.25) + roundDoubleToInt(samplesPerPixel * 30.0)) / 2; //roundDoubleToInt(samplesPerPixel * 25.0);
		
	int multiplySamplesPerPixel = pow(samplesPerPixel * 20.0, 1.1);
	
	int choices[] = { 10, 50, 100, 500, 1000, 5000, 10000, 20000, 50000, 100000, 200000 };
	int choicesSize = numElementsInArray(choices);
		
	for(int i = 0; i < choicesSize; i++) {
		if(choices[i] > multiplySamplesPerPixel)
			return choices[i];
	}
	
	return choices[choicesSize-1];
}



// MyWaveformVerticalRuler //

MyWaveformVerticalRuler::MyWaveformVerticalRuler(bool isLeft_)
	:	isLeft(isLeft_)
{
}

void MyWaveformVerticalRuler::paint(Graphics& g)
{
	g.setColour(Colours::grey);
	g.drawRect(0, 0, getWidth(), getHeight());
	
	g.setColour(Colours::white);
	
	Font font(9);
	g.setFont(font);
	
	double verticalZoom = getParentComponent()->getVerticalZoom();	
	double oneOverVerticalZoom = 1.0 / verticalZoom;
	
	double divisionIncrement = 0.5;
	double subdivisionIncrement = 0.1;
	
	const int halfHeight = getHeight()/2;
	
	Justification just = isLeft ? Justification::topLeft : Justification::topRight;
		
	// subdivisions
	if(verticalZoom >= 0.25) {
		for(double value = 0.0; value <= oneOverVerticalZoom; value += subdivisionIncrement) {
			int distancFromYAxis = roundDoubleToInt(value * verticalZoom * halfHeight);
			int upperY = halfHeight-distancFromYAxis,
				lowerY = halfHeight+distancFromYAxis;
			
			if(isLeft) {
				g.drawHorizontalLine(upperY, 7*getWidth()/8, getWidth());
				if(distancFromYAxis != 0) 
					g.drawHorizontalLine(lowerY, 7*getWidth()/8, getWidth());
			}
			else {
				g.drawHorizontalLine(upperY, 0, getWidth()/8);
				if(distancFromYAxis != 0) 
					g.drawHorizontalLine(lowerY, 0, getWidth()/8);
			}
			
			if(verticalZoom >= 2.0) {
				String valueString(value, 1);
				
				g.drawText(valueString, 1, upperY-font.getHeight()/2+1, getWidth()-2, font.getHeight(), just, false);
				if(distancFromYAxis != 0) 
					g.drawText(valueString, 1, lowerY-font.getHeight()/2+1, getWidth()-2, font.getHeight(), just, false);
			}
		}
	}
	
	// divisions (last in case we eventually change the colours)
	int count = 0;
	for(double value = 0.0; value <= oneOverVerticalZoom; value += divisionIncrement, count++) {
		int distancFromYAxis = roundDoubleToInt(value * verticalZoom * halfHeight);
		int upperY = halfHeight-distancFromYAxis,
			lowerY = halfHeight+distancFromYAxis;
		
		if(isLeft) {
			g.drawHorizontalLine(upperY, 2*getWidth()/3, getWidth());
			if(distancFromYAxis != 0) 
				g.drawHorizontalLine(lowerY, 2*getWidth()/3, getWidth());
		}
		else {
			g.drawHorizontalLine(upperY, 0, getWidth()/3);
			if(distancFromYAxis != 0) 
				g.drawHorizontalLine(lowerY, 0, getWidth()/3);
		}
		
		
		if((verticalZoom >= 0.5) || ((count % 4) == 0)) 
		{
			String valueString(value, 1);
			
			g.drawText(valueString, 1, upperY-font.getHeight()/2+1, getWidth()-2, font.getHeight(), just, false);
			if(distancFromYAxis != 0) 
				g.drawText(valueString, 1, lowerY-font.getHeight()/2+1, getWidth()-2, font.getHeight(), just, false);
		}
		
	}
}





// MyWaveformChannelDisplay //

MyWaveformChannelDisplay::MyWaveformChannelDisplay(WaveformDisplayStyle style_, bool rulerLeft, bool rulerRight)
	:	style(style_),
		bufferMin(0),
		bufferMax(0),
		verticalRulerLeft(0),
		verticalRulerRight(0)//,
		//tempBufferSize(4096),
		//tempBuffer(0)
{				
	if(rulerLeft)
		addAndMakeVisible(verticalRulerLeft = new MyWaveformVerticalRuler(true));
			
	if(rulerRight)
		addAndMakeVisible(verticalRulerRight = new MyWaveformVerticalRuler(false));
	
}

MyWaveformChannelDisplay::~MyWaveformChannelDisplay()
{
	if(bufferMin != 0)
		delete[] bufferMin;
	
	if(bufferMax != 0)
		delete[] bufferMax;
	
	deleteAllChildren();
	
//	if(tempBuffer != 0)
//		delete[] tempBuffer;
}

double MyWaveformChannelDisplay::getVerticalZoom() 
{ 
	ScopedLock sl(lock);
	
	return getParentComponent()->getVerticalZoom(); 
}

void MyWaveformChannelDisplay::paint(Graphics& g)
{	
	// paint waveform here
	ScopedLock sl(lock);
	
	//printf("%p MyWaveformChannelDisplay::paint\n", this);
	
	if(bufferMin != 0 && bufferMax != 0) {
		switch(style) {
			case Blocky:
				paintBlocky(g);
				break;
			case RoundBlocky:
				paintRoundBlocky(g);
				break;
			case NormalAndRoundBlocky:
				paintRoundBlocky(g);
				paintNormal(g);
			case Normal:
			default:
				paintNormal(g);
				break;
		}
	}
}

void MyWaveformChannelDisplay::paintNormal(Graphics& g)
{
	g.setColour(Colours::grey);
	g.drawRect(0, 0, getWidth(), getHeight());
	
	const float swell = 0.5f;
	
	const float halfHeight = getHeight() * 0.5f;
	
	const float multiplier = 1.0f / 0x7fffffff 
		* getParentComponent()->getVerticalZoom() 
		* halfHeight; 
	
	float oldMin = bufferMin[0] * multiplier,
		oldMax = bufferMax[0] * multiplier;
				
	const double bufferInc = jmin(getParentComponent()->getSamplesPerPixel(), 1.0);
	double bufferIndex = 0.0;
	
	int drawX = verticalRulerLeft == 0 ? 0 : RULERWIDTH;
	g.drawLine(drawX, halfHeight, getWidth(), halfHeight);
	
	g.setColour(Colours::lightyellow);
	
	for(int i = 1; i < getWaveformWidth(); i++, drawX++, bufferIndex += bufferInc) {
		int iBufferIndex = (int)bufferIndex;
		
		float valueMin = bufferMin[iBufferIndex] * multiplier;
		float valueMax = bufferMax[iBufferIndex] * multiplier;
		
		g.drawVerticalLine(drawX, 
						   jmin(valueMin, oldMax) + halfHeight, 
						   jmax(valueMax, oldMin) + halfHeight);
		
		oldMin = valueMin + swell;
		oldMax = valueMax - swell;
	}
}

void MyWaveformChannelDisplay::paintBlocky(Graphics& g)
{
	g.setColour(Colours::grey);
	g.drawRect(0, 0, getWidth(), getHeight());
	
	int blockWidth = 3;
	int blockSpace = 1;
	int blockStep = blockWidth+blockSpace;
	
	const float swell = 1.0f;
	
	const float halfHeight = getHeight() * 0.5f;
	
	const float multiplier = 1.0f / 0x7fffffff 
		* getParentComponent()->getVerticalZoom() 
		* halfHeight; 
	
	float oldMin = bufferMin[0] * multiplier,
		oldMax = bufferMax[0] * multiplier;
				
	const double bufferInc = jmin(getParentComponent()->getSamplesPerPixel(), 1.0);
	double bufferIndex = 0.0;
	
	int drawX = verticalRulerLeft == 0 ? 0 : RULERWIDTH;
	g.drawLine(drawX, halfHeight, getWidth(), halfHeight);
	
	g.setColour(Colours::cornflowerblue);
	
	for(int i = 1; i < getWaveformWidth(); i++, drawX += blockStep) {
		
		int iBufferIndex = (int)bufferIndex;
		float valueMin = bufferMin[iBufferIndex] * multiplier;
		float valueMax = bufferMax[iBufferIndex] * multiplier;
		
		bufferIndex += bufferInc;
		for(int j = 1; j < blockStep; j++, bufferIndex += bufferInc) {
			iBufferIndex = (int)bufferIndex;
			
			valueMin = jmin(valueMin, bufferMin[iBufferIndex] * multiplier);
			valueMax = jmax(valueMax, bufferMax[iBufferIndex] * multiplier);
		}
		
		int drawY = jmin(valueMin, oldMax) + halfHeight;
		int drawHeight = jmax(valueMax, oldMin) + halfHeight - drawY;
		
		g.fillRect(drawX, drawY, blockWidth, drawHeight);
				   
		
		oldMin = valueMin + swell;
		oldMax = valueMax - swell;
	}
}

void MyWaveformChannelDisplay::paintRoundBlocky(Graphics& g)
{
	g.setColour(Colours::grey);
	g.drawRect(0, 0, getWidth(), getHeight());
	
	int blockWidth = 10;
	int blockSpace = 1;
	int blockStep = blockWidth+blockSpace;
	
	const float swell = 2.0f;
	
	const float halfHeight = getHeight() * 0.5f;
	
	const float multiplier = 1.0f / 0x7fffffff 
		* getParentComponent()->getVerticalZoom() 
		* halfHeight; 
	
	float oldMin = bufferMin[0] * multiplier,
		oldMax = bufferMax[0] * multiplier;
				
	const double bufferInc = jmin(getParentComponent()->getSamplesPerPixel(), 1.0);
	double bufferIndex = 0.0;
	
	int drawX = verticalRulerLeft == 0 ? 0 : RULERWIDTH;
	g.drawLine(drawX, halfHeight, getWidth(), halfHeight);
	
	g.setColour(Colours::cornflowerblue);
	
	for(int i = 1; i < getWaveformWidth(); i++, drawX += blockStep) {
		
		int iBufferIndex = (int)bufferIndex;
		float valueMin = bufferMin[iBufferIndex] * multiplier;
		float valueMax = bufferMax[iBufferIndex] * multiplier;
		
		bufferIndex += bufferInc;
		for(int j = 1; j < blockStep; j++, bufferIndex += bufferInc) {
			iBufferIndex = (int)bufferIndex;
			
			valueMin = jmin(valueMin, bufferMin[iBufferIndex] * multiplier);
			valueMax = jmax(valueMax, bufferMax[iBufferIndex] * multiplier);
		}
		
		int drawY = jmin(valueMin, oldMax) + halfHeight;
		int drawHeight = jmax(valueMax, oldMin) + halfHeight - drawY;
		
		g.fillRoundedRectangle(drawX, drawY, blockWidth, drawHeight, blockWidth/2);
		
		
		oldMin = valueMin + swell;
		oldMax = valueMax - swell;
	}
}


void MyWaveformChannelDisplay::resized()
{	
	ScopedLock sl(lock);
	
	//printf("%p MyWaveformChannelDisplay::resized()\n", this);
	allocateBuffer();
	
	if(verticalRulerLeft  != 0)
		verticalRulerLeft->setBounds(0, 0, RULERWIDTH, getHeight());
			
	if(verticalRulerRight != 0)
		verticalRulerRight->setBounds(getWidth() - RULERWIDTH, 0, RULERWIDTH, getHeight());
}

int MyWaveformChannelDisplay::getWaveformWidth()
{
	ScopedLock sl(lock);
	
	int width = getWidth();
	
	if(verticalRulerLeft  != 0)	width -= RULERWIDTH;
	if(verticalRulerRight != 0)	width -= RULERWIDTH;
	
	return width;
}

void MyWaveformChannelDisplay::allocateBuffer()
{	
	ScopedLock sl(lock);
	
	if(bufferMin != 0)
		delete[] bufferMin;
	
	if(bufferMax != 0)
		delete[] bufferMax;
		
	// we could use getWaveformWidth() but let's overestimate..
	bufferMin = new int[getWidth()];
	bufferMax = new int[getWidth()];
	
	//printf("%p MyWaveformChannelDisplay::allocateBuffer(%d) bufferMin=%p bufferMax=%p\n", this, getWaveformWidth(), bufferMin, bufferMax);
	
	//for(int i = 0; i < getWaveformWidth(); i++) bufferMin[i] = bufferMax[i] = 0;
}






MyWaveformMarkerLabel::MyWaveformMarkerLabel(String name)
	:	Label(String(name)<<" Label", name)
{	
		//printf("MyWaveformMarkerLabel::MyWaveformMarkerLabel() %p\n", this);
}

/*
void MyWaveformMarkerLabel::componentNameChanged(Component& component)
{
	//printf("MyWaveformMarkerLabel::componentNameChanged\n");
}
*/





// MyWaveformMarkerHandle //

MyWaveformMarkerHandle::MyWaveformMarkerHandle(const String& name, class MyWaveformEditor* editor_, MyWaveformSelection* selection_)
	:	Component(name),
		waveformEditor(editor_),
		selection(selection_),
		shouldDisplayLabelWhenVisible(true),
		markerId(waveformEditor->getNextMarkerId()),
		draggingHandle(0),
		colour(Colours::red),
		snapToZeroCrossings(false),
		originalX(0),
		samplePosition(0)
{
	waveformEditor->addChildComponent(markerLabel = new MyWaveformMarkerLabel(name));
			
	Font font(9);
	markerLabel->setFont(font);
	markerLabel->setSize(200,24);
	markerLabel->setColour(Label::textColourId, colour);
		
	setMouseCursor(MouseCursor::LeftRightResizeCursor);
	setBounds(0, 0, 3, getParentHeight()); // why does setting x = -1 crash on launch?
}

MyWaveformMarkerHandle::~MyWaveformMarkerHandle()
{
	ScopedLock sl(lock);
	
	// not checking the markerLabel was causing a crash on exit occasionally
	if(markerLabel->isValidComponent()) {
		waveformEditor->removeChildComponent(markerLabel);
		delete markerLabel;
	}
}

void MyWaveformMarkerHandle::addListener(MyWaveformMarkerHandleListener* const listener)
{
	//printf("MyWaveformMarkerHandle::addListener this=%p listener=%p\n", this, listener);

    jassert (listener != 0);
    if (listener != 0)
        listeners.add (listener);
}

void MyWaveformMarkerHandle::removeListener(MyWaveformMarkerHandleListener* const listener)
{
	//printf("MyWaveformMarkerHandle::removeListener this=%p listener=%p\n", this, listener);

    listeners.removeValue (listener);
}

void MyWaveformMarkerHandle::sendPositionChanged()
{
    for (int i = listeners.size(); --i >= 0;)
    {
        ((MyWaveformMarkerHandleListener*) listeners.getUnchecked (i))->positionChanged(this);
        i = jmin (i, listeners.size());
    }
}

//void MyWaveformMarkerHandle::handleAsyncUpdate()
//{
//	
//}

void MyWaveformMarkerHandle::visibilityChanged()
{
	ScopedLock sl(lock);
	
	markerLabel->setVisible(shouldDisplayLabelWhenVisible && isVisible());
}

void MyWaveformMarkerHandle::paint(Graphics& g)
{	
	ScopedLock sl(lock);
	//setSize(3, getParentHeight()); // best not have this here
	
	g.setColour(colour);
	g.fillRect(1, 0, 1, getHeight());
	//g.fillRect(2, 0, 1, getHeight());
}

void MyWaveformMarkerHandle::moved()
{	
	ScopedLock sl(lock);
	
	setSize(3, getParentHeight());
	
	if(selection != 0 && draggingHandle == 0 && selection->isUpdatingPositions() == false) {
		MyWaveformMarkerHandle* startSelectionMarker = selection->getStartSelectionMarker();
		MyWaveformMarkerHandle* endSelectionMarker = selection->getEndSelectionMarker();
		
		if(this == startSelectionMarker) {
			if(startSelectionMarker->getPosition() > endSelectionMarker->getPosition())
				endSelectionMarker->setPosition(getPosition());
		}
		else if(this == endSelectionMarker) {
			if(endSelectionMarker->getPosition() < startSelectionMarker->getPosition())
				startSelectionMarker->setPosition(getPosition());
		}
	}
	
	int pixelsX = getX()+1;
	Rectangle waveformBounds = getWaveformDisplayBounds();
	
	if(pixelsX >= 0 && pixelsX < waveformBounds.getWidth()) {
		
		int labelX;
		int leftWidth = getX();
		int rightWidth = getParentWidth() - getRight();
		
		if(leftWidth < rightWidth) {
			markerLabel->setWidth(rightWidth);
			labelX = getRight()-3;
			markerLabel->setJustificationType(Justification::left);
		}
		else {
			markerLabel->setWidth(leftWidth);
			labelX = 3;
			markerLabel->setJustificationType(Justification::right);
		}
		
		int highestMarker = waveformEditor->getHighestMarkerId();	
	
		shouldDisplayLabelWhenVisible = true;
		visibilityChanged();
		
		if(highestMarker > 1) {
			int labelSpacing = (getHeight() - markerLabel->getHeight()) / (highestMarker - 1);
			markerLabel->setTopLeftPosition(labelX + waveformBounds.getX(), 
											waveformBounds.getY() + labelSpacing * (markerId - 1));
		} 
		else {
			markerLabel->setTopLeftPosition(labelX + waveformBounds.getX(), 
											waveformBounds.getY());
		}
	} else {
		shouldDisplayLabelWhenVisible = false;
		visibilityChanged();
	}
	
	getParentComponent()->repaint();
	
	//printf("%p MyWaveformMarkerHandle::moved(x=%d) samples=%d\n", this, getX() + 1, pixelsToSamples(getX() + 1));
}

void MyWaveformMarkerHandle::resized()
{
	//moved();
}

void MyWaveformMarkerHandle::mouseDown(const MouseEvent& e)
{
	ScopedLock sl(lock);
	
	setMouseCursor(MouseCursor::NoCursor);
	
	draggingHandle = this;	
	originalX = getX() + 1 + e.x;
	
	mouseDownScreenX = e.getScreenX();
	mouseDownScreenY = e.getScreenY(); // needed only when reseting the mouse position
	
	ctrlDownLastDrag = e.mods.isCtrlDown();
	enableUnboundedMouseMovement(ctrlDownLastDrag);
	
	//printf("MyWaveformMarkerHandle::mouseDown(x=%d)\n", e.x);
}

void MyWaveformMarkerHandle::mouseDrag(const MouseEvent& e)
{
	ScopedLock sl(lock);
	
	bool ctrlDownThisDrag = e.mods.isCtrlDown();
	
	if(ctrlDownThisDrag != ctrlDownLastDrag) {
		mouseDownScreenX = e.getScreenX();
		originalX = getX() + 1 + e.x;
		ctrlDownLastDrag = ctrlDownThisDrag;
		
		//printf("MyWaveformMarkerHandle::mouseDrag ctrl status changed to %d\n", ctrlDownThisDrag);
		
		setMousePositionToThisHandle();
		
		enableUnboundedMouseMovement(ctrlDownThisDrag);
		
		return;
	}
	
	int distanceFromDragStartX = e.getScreenX() - mouseDownScreenX;
		
	//printf("marker id %d dragged by %d [%d]\n", markerId, e.getDistanceFromDragStartX(), distanceFromDragStartX);
	
	int newPosition;
	
	// samplewise adjustment with ctrl down, pixelwise adjustment otherwise
	if(ctrlDownThisDrag && getSamplesPerPixel() > 1.0) {
		newPosition = pixelsToSamples(originalX) + distanceFromDragStartX;
		//newPosition = pixelsToSamples(originalX) + e.getDistanceFromDragStartX();
	} 
	else {
		newPosition = pixelsToSamples(distanceFromDragStartX + originalX);
		//newPosition = pixelsToSamples(e.getDistanceFromDragStartX() + originalX);
	}
	
	setPosition(newPosition, 
				true,					// update position
				e.mods.isAltDown());	// flip zero crossing snapping if alt is down
		
	if(selection != 0) {
		MyWaveformMarkerHandle* startSelectionMarker = selection->getStartSelectionMarker();
		MyWaveformMarkerHandle* endSelectionMarker = selection->getEndSelectionMarker();
		
		if(this == startSelectionMarker) {
			if(startSelectionMarker->getPosition() > endSelectionMarker->getPosition())
				selection->swapMarkers();
		}
		else if(this == endSelectionMarker) {
			if(endSelectionMarker->getPosition() < startSelectionMarker->getPosition())
				selection->swapMarkers();
		}
	}
}

void MyWaveformMarkerHandle::mouseUp(const MouseEvent& e)
{
	draggingHandle = 0;
	
	setMouseCursor(MouseCursor::LeftRightResizeCursor);
	
	setMousePositionToThisHandle();
	
	enableUnboundedMouseMovement(false);
}

void MyWaveformMarkerHandle::setMousePositionToThisHandle()
{
	Desktop::setMousePosition(getScreenX(), getScreenY()+getHeight()/2);
}

void MyWaveformMarkerHandle::setColour(const Colour& newColour)
{
	ScopedLock sl(lock);
	
	colour = newColour;
	markerLabel->setColour(Label::textColourId, colour);
	repaint();
}

int MyWaveformMarkerHandle::samplesToPixels(int samples)
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->samplesToPixels(samples);
	else
		return selection->samplesToPixels(samples);
}

int MyWaveformMarkerHandle::pixelsToSamples(int pixels)
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->pixelsToSamples(pixels);
	else
		return selection->pixelsToSamples(pixels);
}

int MyWaveformMarkerHandle::limitSamplesToReader(int samplesToLimit)
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->limitSamplesToReader(samplesToLimit);
	else
		return selection->limitSamplesToReader(samplesToLimit);
}

int MyWaveformMarkerHandle::snapToReadersZeroCrossings(int samplesToSnap)
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->snapToReadersZeroCrossings(samplesToSnap);
	else
		return selection->snapToReadersZeroCrossings(samplesToSnap);
}

int MyWaveformMarkerHandle::getReaderNumSamples()
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->getReaderNumSamples();
	else
		return selection->getReaderNumSamples();
}

double MyWaveformMarkerHandle::getSamplesPerPixel()
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->getSamplesPerPixel();
	else
		return selection->getSamplesPerPixel();
}

const Rectangle MyWaveformMarkerHandle::getWaveformDisplayBounds()
{
	ScopedLock sl(lock);
	
	if(selection == 0)
		return ((MyWaveformEditor*)getParentComponent())->getWaveformDisplayBounds();
	else
		return selection->getWaveformDisplayBounds();
}

void MyWaveformMarkerHandle::updatePosition()
{
	ScopedLock sl(lock);
	
	setTopLeftPosition(samplesToPixels(samplePosition) - 1, 0);
}

void MyWaveformMarkerHandle::setPosition(int newSamplePosition, bool shouldUpdatePosition, bool shouldDisableZeroCrossingSnap)
{
	ScopedLock sl(lock);
	
	if(snapToZeroCrossings != shouldDisableZeroCrossingSnap)  //same as logical xor?
		samplePosition = snapToReadersZeroCrossings(limitSamplesToReader(newSamplePosition));
	else
		samplePosition = limitSamplesToReader(newSamplePosition);
	
	if(shouldUpdatePosition) updatePosition();
	
	sendPositionChanged();
}

void MyWaveformMarkerHandle::setShouldDisplayLabelWhenVisible(bool flag)
{
	ScopedLock sl(lock);
	
	shouldDisplayLabelWhenVisible = flag;
	
	markerLabel->setVisible(flag);
	
	repaint();
}




// MyWaveformMultiDisplay //

MyWaveformMultiDisplay::MyWaveformMultiDisplay(WaveformDisplayStyle style_, bool rulerLeft, bool rulerRight, bool rulerAbove, bool rulerBelow)
	:	style(style_),
		reader(0),
		samplesPerPixel(128.0),
		samplesOffset(0.0),
		verticalZoom(1.0),
		hasRulerLeft(rulerLeft),
		hasRulerRight(rulerRight),
		hasRulerAbove(rulerAbove),
		hasRulerBelow(rulerBelow),
		horizontalRulerAbove(0),
		horizontalRulerBelow(0)
{	
}

MyWaveformMultiDisplay::~MyWaveformMultiDisplay()
{
	deleteAllChildren();
	if(reader != 0)
		delete reader;
}

void MyWaveformMultiDisplay::addListener(MyWaveformMultiDisplayListener* const listener)
{
	//printf("MyWaveformMultiDisplay::addListener this=%p listener=%p\n", this, listener);

	jassert (listener != 0);
    if (listener != 0)
        listeners.add (listener);
}

void MyWaveformMultiDisplay::removeListener(MyWaveformMultiDisplayListener* const listener)
{
	//printf("MyWaveformMultiDisplay::removeListener this=%p listener=%p\n", this, listener);

	listeners.removeValue (listener);
}

void MyWaveformMultiDisplay::sendSamplesPerPixelChanged()
{
    for (int i = listeners.size(); --i >= 0;)
    {
        ((MyWaveformMultiDisplayListener*) listeners.getUnchecked (i))->samplesPerPixelChanged(this);
        i = jmin (i, listeners.size());
    }
}

void MyWaveformMultiDisplay::sendSamplesOffsetChanged()
{
    for (int i = listeners.size(); --i >= 0;)
    {
        ((MyWaveformMultiDisplayListener*) listeners.getUnchecked (i))->samplesOffsetChanged(this);
        i = jmin (i, listeners.size());
    }
}

void MyWaveformMultiDisplay::sendVerticalZoomChanged()
{
    for (int i = listeners.size(); --i >= 0;)
    {
        ((MyWaveformMultiDisplayListener*) listeners.getUnchecked (i))->verticalZoomChanged(this);
        i = jmin (i, listeners.size());
    }
}

//void MyWaveformMultiDisplay::handleAsyncUpdate()
//{
//
//}

void MyWaveformMultiDisplay::paint(Graphics& g)
{
	ScopedLock sl(lock);
	
	for(int i = 0; i < channelDisplays.size(); i++) {
		Component* comp = channelDisplays.getUnchecked(i);//->repaint();
		if(comp->isValidComponent())
			comp->repaint();
	}	
}

void MyWaveformMultiDisplay::resized()
{
	ScopedLock sl(lock);
	
	resizeChildren();
	fillBuffers();
}

void MyWaveformMultiDisplay::resizeChildren()
{
	ScopedLock sl(lock);
	
	if(channelDisplays.size() > 0) {
		
		Rectangle waveformBounds = getWaveformBounds();
		
		if(horizontalRulerAbove->isValidComponent()) {
			horizontalRulerAbove->setBounds(waveformBounds.getX(), 
											0, 
											waveformBounds.getWidth(), 
											RULERHEIGHT);
		}
		
		if(horizontalRulerBelow->isValidComponent()) {
			horizontalRulerBelow->setBounds(waveformBounds.getX(), 
											waveformBounds.getBottom(), 
											waveformBounds.getWidth(), 
											RULERHEIGHT);
		}
		
		for(int i = 0; i < channelDisplays.size(); i++) {
			MyWaveformChannelDisplay* channelDisplay = channelDisplays.getUnchecked(i);
			
			channelDisplay->lock.enter();
			
			//printf("%p about to channelDisplay->setBounds\n", channelDisplay);
			
			channelDisplay->setBounds(0, 
									  waveformBounds.getHeight() * i / channelDisplays.size() + waveformBounds.getY(),
									  getWidth(),
									  waveformBounds.getHeight() / channelDisplays.size());
			
			channelDisplay->lock.exit();
			
			//printf("%p done channelDisplay->setBounds\n", channelDisplay);
		}
	}
}

const Rectangle MyWaveformMultiDisplay::getWaveformBounds()
{
	ScopedLock sl(lock);
	
	int x = getX(), 
		y = getY(), 
		w = getWidth(), 
		h = getHeight();
	
	if(hasRulerLeft) {
		x += RULERWIDTH;
		w -= RULERWIDTH;
	}
	
	if(hasRulerRight)
		w -= RULERWIDTH;
	
	if(hasRulerAbove) {
		y += RULERHEIGHT;
		h -= RULERHEIGHT;
	}
	
	if(hasRulerBelow)
		h -= RULERHEIGHT;
	
	return Rectangle(x, y, w, h);
}

void MyWaveformMultiDisplay::setReader(AudioFormatReader* reader_)
{	
	ScopedLock sl(lock);
	
	if(reader != 0)
		delete reader;
	
	reader = reader_;
		
	if(reader != 0 && reader->numChannels != channelDisplays.size()) {
		channelDisplays.clear();
		deleteAllChildren();
		
		horizontalRulerAbove = 0;
		horizontalRulerBelow = 0;
		
		if(hasRulerAbove)
			addAndMakeVisible(horizontalRulerAbove = new MyWaveformHorizontalRuler(true));
		
		if(hasRulerBelow)
			addAndMakeVisible(horizontalRulerBelow = new MyWaveformHorizontalRuler(false));
		
		for(int i = 0; i < reader->numChannels; i++) {
			MyWaveformChannelDisplay* newChannel;
			addAndMakeVisible(newChannel = new MyWaveformChannelDisplay(style, hasRulerLeft, hasRulerRight));
			channelDisplays.add(newChannel);
		}
	}
	
	resizeChildren();
	fillBuffers();
	repaint();
}

void MyWaveformMultiDisplay::fillBuffers()
{
	ScopedLock sl(lock);
	
	//printf("%p MyWaveformMultiDisplay::fillBuffers\n", reader);
		
	if(reader != 0 && channelDisplays.size() > 0) {
		int *buffers[32];

		const int tempBufferSize = jmax(1, (int)samplesPerPixel);
		MemoryBlock tempSpace (tempBufferSize * sizeof (int) * channelDisplays.size() + 64);
				
		for(int c = 0; c < channelDisplays.size(); c++) {
			buffers[c] = ((int*) tempSpace.getData()) + (tempBufferSize * c);
			
			//printf("temp buffer[%d] = %p\n", c, buffers[c]);
		}
		buffers[channelDisplays.size()] = 0;
		
		//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
		
		const int quantisedSamplesOffset = getQuantisedSamplesOffset();
		const double maxSamplesPerPixel = jmax(1.0, samplesPerPixel);
		const int roundedMaxSamplesPerPixel = roundDoubleToInt(maxSamplesPerPixel);
		
		//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
		
		for(int i = 0;  i < getWidth(); i++) {
			
			//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
			
			reader->read(buffers, 
						 (int)(i * maxSamplesPerPixel + quantisedSamplesOffset),
						 roundedMaxSamplesPerPixel);
			
			//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
			
			for(int c = 0; c < channelDisplays.size(); c++) {
				MyWaveformChannelDisplay* channelDisplay = channelDisplays.getUnchecked(c);
				
				//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
				
				int* bufferMin = channelDisplay->getBufferMin();
				int* bufferMax = channelDisplay->getBufferMax();
				int* temp = buffers[c];
				
				//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
				
				bufferMin[i] = bufferMax[i] = temp[0];
				
				for(int j = 1; j < (int)samplesPerPixel; j++) {
					//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
					
					bufferMin[i] = jmin(bufferMin[i], temp[j]);
					bufferMax[i] = jmax(bufferMax[i], temp[j]);
				}
			}
		}
		
		//printf("MyWaveformComponent.cpp line %d\n", __LINE__);
		
	}
	
}

void MyWaveformMultiDisplay::setSamplesPerPixel(double newSamplesPerPixel)
{	
	ScopedLock sl(lock);
	
	if(newSamplesPerPixel > 0.0 && samplesPerPixel != newSamplesPerPixel) {
		samplesPerPixel = newSamplesPerPixel;
		fillBuffers();
		repaint();
		
		sendSamplesPerPixelChanged();
	}
}

void MyWaveformMultiDisplay::setSamplesOffset(double newSamplesOffset)
{	
	ScopedLock sl(lock);
	
	if(newSamplesOffset >= 0.0 && samplesOffset != newSamplesOffset) {
		samplesOffset = newSamplesOffset;
		fillBuffers();	
		repaint();
		
		sendSamplesOffsetChanged();
	}
}

void MyWaveformMultiDisplay::setVerticalZoom(double newVerticalZoom)
{	
	ScopedLock sl(lock);
	
	if(verticalZoom != newVerticalZoom) {
		verticalZoom = newVerticalZoom;
		repaint();		
		
		sendVerticalZoomChanged();
	}
}

int MyWaveformMultiDisplay::getQuantisedSamplesOffset()
{
	ScopedLock sl(lock);
	
	const int roundedMaxSamplesPerPixel = jmax(1, (int)samplesPerPixel);
	const int maxSamplesOffset			= jmax(0.0, (int)reader->lengthInSamples - (getWidth() * samplesPerPixel));
	int quantisedSamplesOffset			= jmin((int)samplesOffset, maxSamplesOffset) / roundedMaxSamplesPerPixel;
	quantisedSamplesOffset			   *= roundedMaxSamplesPerPixel;
	
	return quantisedSamplesOffset;
}

int MyWaveformMultiDisplay::samplesToPixels(int samples)
{
	ScopedLock sl(lock);
	
	if(reader == 0)
		return roundDoubleToInt((samples - samplesOffset) / samplesPerPixel);
	else
		return roundDoubleToInt((samples - getQuantisedSamplesOffset()) / samplesPerPixel);
}

int MyWaveformMultiDisplay::pixelsToSamples(int pixels)
{
	ScopedLock sl(lock);
	
	if(reader == 0)
		return roundDoubleToInt(pixels * samplesPerPixel + samplesOffset);
	else
		return roundDoubleToInt(pixels * samplesPerPixel) + getQuantisedSamplesOffset();
}

int MyWaveformMultiDisplay::limitSamplesToReader(int samplesToLimit)
{
	ScopedLock sl(lock);
	
	if(reader == 0)
		return 0;
	else
		return jlimit(0, (int)reader->lengthInSamples, samplesToLimit);
}

int MyWaveformMultiDisplay::snapToReadersZeroCrossings(const int samplesToSnap, const int tempBufferSize)
{
	ScopedLock sl(lock);
	
	if(reader == 0 || tempBufferSize > 65536)
		return samplesToSnap;
	else {
		int *buffers[32];
		
		//const int tempBufferSize = 512;
		const int halfTempBufferSize = tempBufferSize >> 1;
		
		MemoryBlock tempSpace (tempBufferSize * sizeof (int) * channelDisplays.size() + 64);
		
		for(int c = 0; c < channelDisplays.size(); c++) {
			buffers[c] = ((int*) tempSpace.getData()) + (tempBufferSize * c);
			
			//printf("temp buffer[%d] = %p\n", c, buffers[c]);
		}
		buffers[channelDisplays.size()] = 0;
				
		reader->read(buffers, 
				 samplesToSnap - halfTempBufferSize,
				 tempBufferSize);
		
		int currentRelativePosition = halfTempBufferSize;
		int valueAtSamplesToSnap = buffers[0][currentRelativePosition]; // look only at channel 0
		
		int nextXerox = -1;
		
		currentRelativePosition++;
		while(currentRelativePosition < tempBufferSize && nextXerox == -1) {
			
			int valueAtCurrentRelativePosition = buffers[0][currentRelativePosition];
			
			if((valueAtSamplesToSnap <= 0 && valueAtCurrentRelativePosition >= 0) 
			   ||
			   (valueAtSamplesToSnap >= 0 && valueAtCurrentRelativePosition <= 0))
			{
				nextXerox = currentRelativePosition - halfTempBufferSize + samplesToSnap;
			}
			
			currentRelativePosition++;
		}
		
		int prevXerox = -1;
		
		currentRelativePosition = halfTempBufferSize-1;
		while(currentRelativePosition >= 0 && prevXerox == -1) {
			
			int valueAtCurrentRelativePosition = buffers[0][currentRelativePosition];
			
			if((valueAtSamplesToSnap <= 0 && valueAtCurrentRelativePosition >= 0) 
			   ||
			   (valueAtSamplesToSnap >= 0 && valueAtCurrentRelativePosition <= 0))
			{
				prevXerox = currentRelativePosition - halfTempBufferSize + samplesToSnap + 1;
			}
			
			currentRelativePosition--;
		}
		
		if(nextXerox == -1 && prevXerox == -1)
			return snapToReadersZeroCrossings(samplesToSnap, tempBufferSize << 1); // recursive stopping when tempBufferSize > 65536
		else if(nextXerox == -1)
			return prevXerox;
		else if(prevXerox == -1)
			return nextXerox;
		else if((nextXerox-samplesToSnap) < (samplesToSnap-prevXerox))
			return nextXerox;
		else
			return prevXerox;
		
		jassert(false); // shouldn't get this far!
		
		return samplesToSnap;
		
	}
}

int MyWaveformMultiDisplay::getReaderNumSamples()
{
	ScopedLock sl(lock);
	
	if(reader == 0)
		return 0;
	else 
		return (int)reader->lengthInSamples;
}





// MyWaveformSelection //

MyWaveformSelection::MyWaveformSelection(MyWaveformEditor* editor_)
	:	waveformEditor(editor_),
		draggingHandle(0),
		colour(Colours::red),
		updatingPositions(false),
		displayMarkersAndSelection(false)
{				
	addChildComponent(startSelectionMarker = new MyWaveformMarkerHandle(T("Selection start"), waveformEditor, this));
	addChildComponent(endSelectionMarker = new MyWaveformMarkerHandle(T("Selection end"), waveformEditor, this));
	
	startSelectionMarker->addListener(this);
	endSelectionMarker->addListener(this);
}

MyWaveformSelection::~MyWaveformSelection()
{
	startSelectionMarker->removeListener(this);
	endSelectionMarker->removeListener(this);
}

void MyWaveformSelection::addListener(MyWaveformSelectionListener* const listener)
{
    //printf("MyWaveformSelection::addListener this=%p listener=%p\n", this, listener);
	
	jassert (listener != 0);
    if (listener != 0) {
        listeners.add (listener);
	}
}

void MyWaveformSelection::removeListener(MyWaveformSelectionListener* const listener)
{
	//printf("MyWaveformSelection::removeListener this=%p listener=%p\n", this, listener);
	
	listeners.removeValue (listener);
}

//void MyWaveformSelection::sendSelectionChanged()
//{
//    for (int i = listeners.size(); --i >= 0;)
//    {
//        ((MyWaveformSelectionListener*) listeners.getUnchecked (i))->selectionChanged (this);
//        i = jmin (i, listeners.size());
//    }
//}

//void MyWaveformSelection::handleAsyncUpdate()
//{
//	//cancelPendingUpdate();
//	
//    for (int i = listeners.size(); --i >= 0;)
//    {
//        ((MyWaveformSelectionListener*) listeners.getUnchecked (i))->selectionChanged (this);
//        i = jmin (i, listeners.size());
//    }
//}

	
void MyWaveformSelection::sendSelectionChanged()
{
    for (int i = listeners.size(); --i >= 0;)
    {
        ((MyWaveformSelectionListener*) listeners.getUnchecked (i))->selectionChanged(this);
        i = jmin (i, listeners.size());
    }
}

void MyWaveformSelection::positionChanged (MyWaveformMarkerHandle* /* marker */)
{	
	sendSelectionChanged();
}


void MyWaveformSelection::visibilityChanged()
{
	ScopedLock sl(lock);
	
	if(displayMarkersAndSelection) {
		startSelectionMarker->setVisible(isVisible());
		endSelectionMarker->setVisible(isVisible());
	}
}

void MyWaveformSelection::paint(Graphics& g)
{
	ScopedLock sl(lock);
	
	g.setColour(colour);
	g.setOpacity(0.25f);
	
	//printf("selection %d %d\n", startSelectionMarker->getX() + 1, endSelectionMarker->getX() + 1);
	
	g.fillRect(startSelectionMarker->getX()+1, 
//	g.fillRect(startSelectionMarker->getX()+2, 
			   0, 
			   endSelectionMarker->getX()-startSelectionMarker->getX(), 
			   getHeight());
}

void MyWaveformSelection::resized()
{
	ScopedLock sl(lock);
	
	startSelectionMarker->moved();
	endSelectionMarker->moved();
}

void MyWaveformSelection::mouseDown(const MouseEvent& e)
{
	ScopedLock sl(lock);
	
	if(pixelsToSamples(e.x) > getReaderNumSamples()) return;
	
	displayMarkersAndSelection = true;
	visibilityChanged();
	
	setMouseCursor(MouseCursor::NoCursor);
	
	if(e.mods.isShiftDown()) {		
		int distanceToStart = abs(startSelectionMarker->getX() + 1 - e.x);
		int distanceToEnd = abs(endSelectionMarker->getX() + 1 - e.x);
		
		if(distanceToStart < distanceToEnd) {
			startSelectionMarker->setPosition(pixelsToSamples(e.x), true, e.mods.isAltDown());
			draggingHandle = startSelectionMarker;
		} else {
			endSelectionMarker->setPosition(pixelsToSamples(e.x), true, e.mods.isAltDown());
			draggingHandle = endSelectionMarker;
		}
		
	} 
	else {
		startSelectionMarker->setPosition(pixelsToSamples(e.x), true, e.mods.isAltDown());
		endSelectionMarker->setPosition(pixelsToSamples(e.x), true, e.mods.isAltDown());
		
		draggingHandle = endSelectionMarker;
	}
	
	draggingHandle->mouseDown(e.getEventRelativeTo(draggingHandle));
}

void MyWaveformSelection::mouseDrag(const MouseEvent& e)
{
	ScopedLock sl(lock);
	
	if(draggingHandle != 0)
		draggingHandle->mouseDrag(e.getEventRelativeTo(draggingHandle));
}

void MyWaveformSelection::mouseUp(const MouseEvent& e)
{
	ScopedLock sl(lock);
	
	if(draggingHandle != 0) {
		//draggingHandle->setMouseCursor(MouseCursor::LeftRightResizeCursor);
		draggingHandle->mouseUp(e.getEventRelativeTo(draggingHandle));
	}
	
	draggingHandle = 0;
	
	setMouseCursor(MouseCursor::NormalCursor);
}

void MyWaveformSelection::setColour(const Colour& newColour)
{
	ScopedLock sl(lock);
	
	colour = newColour;
	
	startSelectionMarker->setColour(colour);
	endSelectionMarker->setColour(colour);
	
	repaint();
}

int MyWaveformSelection::samplesToPixels(int samples)
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->samplesToPixels(samples);
}

int MyWaveformSelection::pixelsToSamples(int pixels)
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->pixelsToSamples(pixels);
}

int MyWaveformSelection::limitSamplesToReader(int samplesToLimit)
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->limitSamplesToReader(samplesToLimit);
}

int MyWaveformSelection::snapToReadersZeroCrossings(int samplesToSnap)
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->snapToReadersZeroCrossings(samplesToSnap);
}

int MyWaveformSelection::getReaderNumSamples()
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->getReaderNumSamples();
}

double MyWaveformSelection::getSamplesPerPixel()
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->getSamplesPerPixel();
}

const Rectangle MyWaveformSelection::getWaveformDisplayBounds()
{
	ScopedLock sl(lock);
	
	return ((MyWaveformEditor*)getParentComponent())->getWaveformDisplayBounds();
}

void MyWaveformSelection::updatePositions()
{
	ScopedLock sl(lock);
	
	startSelectionMarker->updatePosition();
	endSelectionMarker->updatePosition();
}

void MyWaveformSelection::swapMarkers()
{
	ScopedLock sl(lock);
	
	//printf("swapping markers\n");
	
	MyWaveformMarkerHandle* temp;
	temp = startSelectionMarker;
	startSelectionMarker = endSelectionMarker;
	endSelectionMarker = temp;
	
	String tempName = startSelectionMarker->getName();
	startSelectionMarker->setName(endSelectionMarker->getName());
	endSelectionMarker->setName(tempName);
	
	String tempText = startSelectionMarker->getLabelName();
	startSelectionMarker->setLabelName(endSelectionMarker->getLabelName());
	endSelectionMarker->setLabelName(tempText);
	
	int tempId = startSelectionMarker->markerId;
	startSelectionMarker->markerId = endSelectionMarker->markerId;
	endSelectionMarker->markerId = tempId;
	
	startSelectionMarker->moved();
	endSelectionMarker->moved();
}

void MyWaveformSelection::setPositions(int start, int end)
{
	ScopedLock sl(lock);
	
	updatingPositions = true;
	startSelectionMarker->setPosition(start);
	endSelectionMarker->setPosition(end);
	updatePositions();
	updatingPositions = false;
}





// MyWaveformEditor //


MyWaveformEditor::MyWaveformEditor(WaveformDisplayStyle style_, bool rulerLeft, bool rulerRight, bool rulerAbove, bool rulerBelow)
	:	style(style_),
		markerIdCount(0)
{
	addAndMakeVisible(waveformDisplay = new MyWaveformMultiDisplay(style, rulerLeft, rulerRight, rulerAbove, rulerBelow));	
	addChildComponent(selection = new MyWaveformSelection(this));
	selection->setColour(Colours::lightgreen);
		
	addAndMakeVisible(resizer = new ResizableCornerComponent(this, &sizeLimits));
	//sizeLimits.setSizeLimits(RULERSIZE*3, RULERSIZE*3, 0xffffff, 0xffffff);
	sizeLimits.setSizeLimits(RULERWIDTH*3,RULERHEIGHT*3,0xffffff,0xffffff);
}

MyWaveformEditor::~MyWaveformEditor()
{
	deleteAllChildren();
}

void MyWaveformEditor::addListener(MyWaveformEditorListener* const listener)
{
    //printf("MyWaveformEditor::addListener this=%p listener=%p\n", this, listener);
	
	jassert (listener != 0);
    if (listener != 0) {
        listeners.add (listener);
		
		if(dynamic_cast <MyWaveformSelectionListener*> (listener) != 0)
			selection->addListener(listener);
				
		if(dynamic_cast <MyWaveformSelectionListener*> (listener) != 0)
			waveformDisplay->addListener(listener);
	}
	
}

void MyWaveformEditor::removeListener(MyWaveformEditorListener* const listener)
{
	//printf("MyWaveformEditor::removeListener this=%p listener=%p\n", this, listener);
	
	listeners.removeValue (listener);
	
	if(dynamic_cast <MyWaveformSelectionListener*> (listener) != 0)
		selection->removeListener(listener);
			
	if(dynamic_cast <MyWaveformSelectionListener*> (listener) != 0)
		waveformDisplay->removeListener(listener);

}

//void MyWaveformEditor::handleAsyncUpdate()
//{
//	/*
//	//cancelPendingUpdate();
//	
//    for (int i = listeners.size(); --i >= 0;)
//    {
//        ((SliderListener*) listeners.getUnchecked (i))->sliderValueChanged (this);
//        i = jmin (i, listeners.size());
//    }
//	 */
//}

void MyWaveformEditor::paint(Graphics& g)
{
	ScopedLock sl(lock);
	
	g.setColour(Colours::darkgrey);
	g.fillRect(0, 0, getWidth(), getHeight());	
}

void MyWaveformEditor::resized()
{	
	ScopedLock sl(lock);
	
	waveformDisplay->setBounds(0,0, getWidth(), getHeight());
	resizer->setBounds(getWidth()-12,getHeight()-12,12,12);
	
	selection->setBounds(waveformDisplay->getWaveformBounds());
	selection->updatePositions();
}

void MyWaveformEditor::setReader(AudioFormatReader* reader_)
{
	ScopedLock sl(lock);
	
	waveformDisplay->setReader(reader_);
	
	if(reader_ != 0) {
		setSelection(0,0); // still not converting pixels correctly?
		selection->setVisible(true);
	} else
		selection->setVisible(false);

}

void MyWaveformEditor::setSamplesPerPixel(double newSamplesPerPixel)
{
	ScopedLock sl(lock);
	
	waveformDisplay->setSamplesPerPixel(newSamplesPerPixel);
	selection->updatePositions();
}

void MyWaveformEditor::setSamplesOffset(double newSamplesOffset)
{
	ScopedLock sl(lock);
	
	waveformDisplay->setSamplesOffset(newSamplesOffset);
	selection->updatePositions();
}

void MyWaveformEditor::setVerticalZoom(double newVerticalZoom)
{
	ScopedLock sl(lock);
	
	waveformDisplay->setVerticalZoom(newVerticalZoom);
}

int MyWaveformEditor::samplesToPixels(int samples)
{
	ScopedLock sl(lock);
	
	return waveformDisplay->samplesToPixels(samples);
}

int MyWaveformEditor::pixelsToSamples(int pixels)
{
	ScopedLock sl(lock);
	
	return waveformDisplay->pixelsToSamples(pixels);
}

int MyWaveformEditor::limitSamplesToReader(int samplesToLimit)
{
	ScopedLock sl(lock);
	
	return waveformDisplay->limitSamplesToReader(samplesToLimit);
}

int MyWaveformEditor::snapToReadersZeroCrossings(int samplesToSnap)
{
	ScopedLock sl(lock);
	
	return waveformDisplay->snapToReadersZeroCrossings(samplesToSnap);
}

int MyWaveformEditor::getReaderNumSamples()
{
	ScopedLock sl(lock);
	
	return waveformDisplay->getReaderNumSamples();
}

double MyWaveformEditor::getSamplesPerPixel()
{
	return waveformDisplay->getSamplesPerPixel();
}

const Rectangle MyWaveformEditor::getWaveformDisplayBounds()
{
	ScopedLock sl(lock);
	
	return waveformDisplay->getWaveformBounds();
}

void MyWaveformEditor::getSelection(int& start, int& end)
{
	ScopedLock sl(lock);
	
	start = selection->getStartSelectionMarker()->getPosition();
	end = selection->getEndSelectionMarker()->getPosition();
}

void MyWaveformEditor::setSelection(int start, int end)
{
	ScopedLock sl(lock);
	
	selection->setPositions(start, end);
}

int MyWaveformEditor::getHighestMarkerId()
{
	ScopedLock sl(lock);
	
	return markerIdCount;
}

int MyWaveformEditor::getNextMarkerId()
{
	ScopedLock sl(lock);
	
	markerIdCount++;
	
	return markerIdCount;
}





